#include "PlayerManager.h"
#include "Player.h"
#include "ServiceProvider.h"
#include "GameSettings.h"
#include "LeagueManager.h"
#include "NameManager.h"
#include "Team.h"
#include "PlayerFunctors.h"

#include <set>
#include <algorithm>
#include <cassert>

size_t PlayerManager::NUMBER_OF_PLAYERS = 0;
const unsigned int PlayerManager::SLOT_PLAYERS = 100;

PlayerManager::PlayerManager(ServiceProvider* const i_sp) : m_sp(i_sp)
{
	assert(m_sp);
}

PlayerManager::~PlayerManager()
{
	Clear();
}

void PlayerManager::Initialize(const size_t i_startID)
{
	NUMBER_OF_PLAYERS = i_startID;

	Player::s_params.MAX_CHAR_PLAYER_NAME	= m_sp->GetSettings().GetValue("I_MAX_CHAR_PLAYER_NAME").Get<size_t>();
	Player::s_params.DAILY_FATIGUE_RECOVER	= m_sp->GetSettings().GetValue("I_DAILY_FATIGUE_RECOVER").Get<size_t>();
	Player::s_params.MIN_SKILL_VALUE		= m_sp->GetSettings().GetValue("I_MIN_SKILL_VALUE").Get<size_t>();
	Player::s_params.SELL_FACTOR			= m_sp->GetSettings().GetValue("F_PLAYER_SELL_FACTOR");
	Player::s_params.BUY_FACTOR				= m_sp->GetSettings().GetValue("F_PLAYER_BUY_FACTOR");

	GenerateNewPlayerSlot();
}

void PlayerManager::Clear()
{
	Utilities::DeepClear(m_freePlayers);
	Utilities::DeepClear(m_busyPlayers);
	Utilities::DeepClear(m_hofPlayers);
	NUMBER_OF_PLAYERS = 0;
}

Player*	PlayerManager::GetNewPlayer()
{
	if(m_freePlayers.empty())
		GenerateNewPlayerSlot();

	Player* p = m_freePlayers.back();
	m_freePlayers.pop_back();
	m_busyPlayers.push_back(p);

	return p;
}

Player*	PlayerManager::CreateNewPlayer(const bool i_isEmpty, const bool i_isBusy)
{
	Player* p = i_isEmpty ? new Player() : new Player(m_sp->GetNameMgr().GetAPlayerName(), GetNewID());
	if(i_isBusy)
		m_busyPlayers.push_back(p);
	else
		m_freePlayers.push_back(p);
	return p;
}

void PlayerManager::GenerateNewPlayerSlot()
{
	m_freePlayers.reserve(m_freePlayers.size() + SLOT_PLAYERS);
	for(size_t i = 0; i < SLOT_PLAYERS; ++i)
		CreateNewPlayer(false, false);
}

void PlayerManager::IngagePlayer(Player* i_player)
{
	assert(std::find(m_busyPlayers.begin(), m_busyPlayers.end(), i_player) == m_busyPlayers.end());
	m_busyPlayers.push_back(i_player);
	Utilities::RemoveAll(m_freePlayers, i_player);
}

void PlayerManager::SellPlayer(Player* i_player)
{
	assert(std::find(m_freePlayers.begin(), m_freePlayers.end(), i_player) == m_freePlayers.end());

	m_freePlayers.push_back(i_player);
	i_player->SetTeam(0);
	
	PlayerVecIt it = std::find(m_busyPlayers.begin(), m_busyPlayers.end(), i_player);
	assert(it != m_busyPlayers.end());
	m_busyPlayers.erase(it);
}

void PlayerManager::UpdateFreePlayers()
{
	for(PlayerVecIt it = m_freePlayers.begin(); it != m_freePlayers.end(); )
	{
		const bool player_out = (*it)->SeasonUpdate();

		if(player_out)
		{
			Player* p = *it;
			it = m_freePlayers.erase(it);
			delete p;
		}
		else
			++it;
	}
}

struct SamePlayerID
{
	SamePlayerID(size_t i_id) : m_id(i_id){}

	bool operator()(const Player* const i_player) const
	{
		return i_player->GetId() == m_id;
	}

	size_t m_id;
};

Player*	PlayerManager::GetPlayerFromID(const size_t i_playerID)
{
	PlayerVecIt it = std::find_if(m_busyPlayers.begin(), m_busyPlayers.end(), SamePlayerID(i_playerID));
	if(it != m_busyPlayers.end())
		return (*it);

	it = std::find_if(m_freePlayers.begin(), m_freePlayers.end(), SamePlayerID(i_playerID));
	if(it != m_busyPlayers.end())
		return (*it);

	assert(false);
	return 0;
}

void PlayerManager::Update()
{
	for(PlayerVecIt it = m_freePlayers.begin(); it!= m_freePlayers.end(); ++it)
		(*it)->Heal();
}

void PlayerManager::EndSeasonUpdate()
{
	PlayerVec retiringPlayers;

	for(PlayerVecIt it = m_busyPlayers.begin(); it!= m_busyPlayers.end(); ++it)
	{
		if((*it)->SeasonUpdate())
		{
			retiringPlayers.push_back(*it);
			*it = 0;
		}
	}
	Utilities::RemoveAll(m_busyPlayers, 0);

	for(PlayerVecIt it = m_freePlayers.begin(); it!= m_freePlayers.end(); ++it)
	{
		if((*it)->SeasonUpdate())
		{
			retiringPlayers.push_back(*it);
			*it = 0;
		}
	}
	Utilities::RemoveAll(m_freePlayers, 0);

	for(PlayerVecIt it = retiringPlayers.begin(); it!= retiringPlayers.end(); ++it)
	{
		Player* player = *it;
		player->SetRetirementYear(m_sp->GetLeagueMgr().GetCurrentSeason());
		Team* playerTeam = player->GetTeam();
		if(playerTeam && !playerTeam->RemovePlayer(player))
		{
			Player* newPlayer = GetNewPlayer();
			const bool playerAdded = playerTeam->AddPlayer(newPlayer);
			assert(playerAdded); playerAdded;
			const bool playerRemoved = playerTeam->RemovePlayer(player);
			assert(playerRemoved); playerRemoved;
			player->SetTeam(0);
		}
		if(player->GetTotalPlayedMatch() < 100)
			delete player;
		else
			m_hofPlayers.push_back(player);
	}

	ShrinkHofPlayers();
	GenerateNewPlayerSlot();
}

void PlayerManager::ShrinkHofPlayers()
{
	static const int NUM_HOF_PLAYERS = Player::NUM_HOF_PLAYERS;
	std::sort(m_hofPlayers.begin(), m_hofPlayers.end(), PlayerComparisionTotalMatch);

	if(m_hofPlayers.size() <= NUM_HOF_PLAYERS)
		return;

	std::set<Player*> toKeep;
	toKeep.insert(m_hofPlayers.begin(), m_hofPlayers.begin() + NUM_HOF_PLAYERS);
	
	std::sort(m_hofPlayers.begin(), m_hofPlayers.end(), PlayerComparisionTotalGoal);
	toKeep.insert(m_hofPlayers.begin(), m_hofPlayers.begin() + NUM_HOF_PLAYERS);

	std::sort(m_hofPlayers.begin(), m_hofPlayers.end(), PlayerComparisionTotalGoalRatio);
	toKeep.insert(m_hofPlayers.begin(), m_hofPlayers.begin() + NUM_HOF_PLAYERS);
	
	std::sort(m_hofPlayers.begin(), m_hofPlayers.end(), PlayerComparisionAge);
	toKeep.insert(m_hofPlayers.begin(), m_hofPlayers.begin() + NUM_HOF_PLAYERS);
	
	std::sort(m_hofPlayers.begin(), m_hofPlayers.end(), PlayerComparisionNumSeason);
	toKeep.insert(m_hofPlayers.begin(), m_hofPlayers.begin() + NUM_HOF_PLAYERS);
	
	std::sort(m_hofPlayers.begin(), m_hofPlayers.end(), PlayerComparisionNumInjury);
	toKeep.insert(m_hofPlayers.begin(), m_hofPlayers.begin() + NUM_HOF_PLAYERS);

	std::sort(m_hofPlayers.begin(), m_hofPlayers.end());
	PlayerVecIt vit = m_hofPlayers.begin();
	for(std::set<Player*>::iterator it= toKeep.begin(); it!= toKeep.end(); ++it)
	{
		while(*it != *vit)
		{
			delete *vit;
			*vit = 0;
			++vit;
		}
		++vit;
	}
	while(vit != m_hofPlayers.end())
	{
		delete *vit;
		*vit = 0;
		++vit;
	}

	Utilities::RemoveAll(m_hofPlayers, static_cast<Player*>(0));
	assert(m_hofPlayers.size() == toKeep.size());
	std::sort(m_hofPlayers.begin(), m_hofPlayers.end(), PlayerComparisionTotalMatch);
}
